$mod186
$ep
NAME Keypad

;IO Setup for 80C188 
	UMCR    EQU    0FFA0H ; Upper Memory Control Register
	LMCR    EQU    0FFA2H ; Lower Memory control Register         
	PACS    EQU    0FFA4H ; Peripheral Chip Select Base Address
	MPCS    EQU    0FFA8H ; MMCS and PCS Alter Control Register
	MMCS    EQU    0FFA6H ; MMCS control register
	
	PORTA8255 EQU 080H ; port a
	PORTB8255 EQU 081H; port b
	PORTC8255 EQU 082H; port c
	CWR8255 EQU 083H; command word register
	
	ACK1 EQU 33H
	ACK2 EQU 55H
	
	LCD0 EQU 0100H ;LCD rs = 0
	LCD1 EQU 0101H ;LCD rs = 1
	
	CHOOSE_LED EQU 0100H
	CHOOSE_SEG EQU 0180H
	
	DAC EQU 100H
	
	T2CNT EQU 0FF60H
	T2CMPA EQU 0FF62H
	T2CON EQU 0FF66H
	TCUCON EQU 0FF32H
	
	EOI EQU 0FF22H ; End of interrupt Register
	
	;Serial port definition and initialize 
         SRB     EQU       00H ; Serial Receiver Buffer Register (R)
         STB     EQU       00H ; Serial Transmitter Holding Register(W)  
         SIER    EQU       01H ; Serial Interrupte Enable Register (w)
         IIR     EQU       02H ; Interrupt Identification Register (R)
         SMD     EQU       03H ; Serial Line Control Register
         SST     EQU       05H ; Serial Line Status Register
         DLL     EQU       00H ; Divisor Latch Least Significant BYTE
         DLM     EQU       01H ; Divisor Latch most  Significant BYTE

;Definition of content of SST register
;|Not Use|TE|THRE|BI|FE|PE|OE|DR|
;TE Transmitter empty
;THRE Transmittor Holding Register Empty
;BI Breakr Interrupt
;FE Framing Error
;PE Parity Error
;OE Overrun Error 
;DR Data Ready
          REC_RDY    EQU   00000001B
          TRAN_RDY   EQU   00100000B
          ERR_DET    EQU   00001110B
          BREAK_DET  EQU   00010000B
; Serial Line Control Data
        SMD_DATA     EQU    00000111B
        S_INT_ENA    EQU    00000011B
        S_INT_DIS    EQU    00000000B
	
	
;Interrupt Control Registers
      

	INT3_CTRL	EQU 0FF3EH ;Interrupt 3 Control Register
	INT2_CTRL	EQU 0FF3CH
	INT1_CTRL	EQU 0FF3AH
	INT0_CTRL	EQU 0FF38H
	TIMER_CTRL	EQU 0FF32H ;Timer Interrupt Control Register
	ISR		EQU 0FF30H ; Interrupt Status Register
  ;      EOI             EQU 0FF22H ; END OF INTERRUPT REGISTER
	
        IMKW            EQU 0FF28H ; Interrupt Mask 
        IPMK            EQU 0FF2Ah ; Interrupt Priority Mask 	
	
; STACK SEGMENT
STACK_SEG		SEGMENT
	db 128 DUP(?)
	tos label word	
STACK_SEG		ENDS
	
; DATA SEGMENT
DATA_SEG SEGMENT 
	AUDIO_FLAG db 0
	AUDIO_START dw 0
	AUDIO_CNT dw 0

	T2_CNT db 0
	LED_SELECT db 01H, 02H, 04H, 08H, 010H, 020H
	LED_DISP db 00100000b, 00101111b, 00010010b, 00000110b, 00001101b, 01000100b
	LED_INDEX db 0
	
	ACK_COUNT_LEN EQU 2
	ACK_CUR_COUNT db 2
	
	RECV_BYTES DB 100 DUP(0)
	RECV_INDEX DB 0
	rec_state db 0
	recv_flag db 0

	send_ready db 0

	keyin db 0ffh
	userpw db 4 dup(?)
	userid db 4 dup(?)
DATA_SEG ENDS

INT_RAM_AREA	SEGMENT
	QUEUE_LEN	EQU	128

	QUEUE_TRANS	DB	QUEUE_LEN DUP(?)
	QUEUE_HEAD	DW	0H
	QUEUE_TAIL	DW	0H
INT_RAM_AREA	ENDS


;CODE SEGMENT
CODE_SEG        SEGMENT
      
PUBLIC	START 

ASSUME  CS:CODE_SEG, SS:STACK_SEG
CLI

START:

	MOV DS:RECV_INDEX, 0
; Initialize MPCS to MAP peripheral to IO address
	MOV DX, MPCS
	MOV AX, 4083H ; Set MCS block size to 512KB
	OUT DX, AL

; Initialize MMCS
	MOV DX, MMCS
	MOV AX, 8003H ; MCS start at 80000H
	OUT DX, AL

; PACS initial, set the parallel port start from 00H
	MOV DX, PACS
	MOV AX, 0007H ; xPeripheral starting address 00H no READY, No Waits
	OUT DX, AL

	; YOUR CODE HERE ...
	; GOOD LUCK!
	
	;ss intialization
	mov DX, STACK_SEG
	mov SS, DX
	mov SP, offset tos
	
	;ds initialization
	mov DX, DATA_SEG
	mov DS, DX
	
	;initialize 8255 CWR
	mov DX, CWR8255
	MOV AL, 10001001b ;port A, B Output, port C Input
	OUT DX, AL
	
	;Set up IVT
	mov AX, 0H
	mov ES, AX
	
	;timer 2 interrupt, starts at 4CH
	mov BX, 04CH
	mov word ptr ES:[BX], offset TIMER2_ISR
	mov BX, 04EH
	mov word ptr ES:[BX], seg TIMER2_ISR
	
	mov BX, 030H
	mov word ptr ES:[BX], offset SERIAL_ISR
	mov BX, 032H
	mov word ptr ES:[BX], seg SERIAL_ISR
	
	;initialize timer 2
	
	mov AX, 0H
	mov DX, T2CNT
	out DX, AL
	
	mov AX, 250
	mov DX, T2CMPA
	out DX, AL
	
	mov AX, 0E001H
	mov DX, T2CON
	out DX, AL
	
	mov AX, 00H
	mov DX, TCUCON
	out DX, AL

	; Initialize SCU for operation
		MOV	AL,SMD_DATA
		OUT	SMD,AL
; Enable serial interrupts
		MOV	AL,S_INT_ENA
		OUT	SIER,AL
; =============== INITIALIZATION OF INTERRUPT CONTROL UNIT =============
; Initialize ICU for operation
		
; Mask all interrupts except SCU
                ;disable TX interrupt,ENABLE RX.
		MOV	AL,1
		OUT	SIER,AL
; SCU use INT0, enable INT0		
	        MOV DX, INT0_CTRL
  		XOR	AX,AX
         	OUT	DX,AL
; Mask other Int
                CLI
                MOV	DX,IMKW
		MOV	AX,0EEH
		OUT 	DX,AL   
	
;IDLE: jmp IDLE
	
	;clear GPRs
	xor AX, AX
	xor BX, BX
	xor CX, CX
	xor DX, DX

	mov DX, 8000H
	mov ES, DX
	mov DI, 0H
	
	mov DX, PORTA8255
	mov AX, 0FFFFH
	out DX, AL
	
	STI

;----- Start of the routine-------
LOGIN:
	xor bx, bx
	
	get_cred:
		mov cx, 4
		mov si, 0

		get_start:
			push cx
			push si
			jmp poll1
			getid_keyret:
				pop si
				pop cx

			mov al, DS:keyin

			cmp bl, 0
			jne getpw
			mov DS:userid[si], al
			jmp ret_get
			
			getpw:
				mov DS:userpw[si], al
			
			ret_get:
				inc si
				
			NOPOLL:	
				cmp DS:AUDIO_FLAG, 1
				je NOPOLL
		loop get_start
		
		inc bl
		cmp bl, 2
	jb get_cred
	
	mov cx, 4
	mov si, 0
	
	print_userid:
		mov al, DS:userid[si]
		call far ptr print_char
		
		inc si
	loop print_userid

	mov cx, 4
	mov si, 0
	
	print_userpw:
		mov al, DS:userpw[si]
		call far ptr print_char
		
		inc si
	loop print_userpw
	
	mov DS:send_ready, 1

	idle_sendidpw: ; while the user has not been verified
		cmp DS:send_ready, 1
	je idle_sendidpw

jmp LOGIN
	
POLL1: ; key asterick
	mov DX, PORTB8255
	mov AX, 00000001b
	out DX, AL
	
	mov DX, PORTC8255
	in AX, DX
	
	; Check for assertions 1
	;row1
	cmp AL, 00000001b
	jne POLL1A
	
	mov DS:AUDIO_START, 8D48H
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 10
	jmp NEAR PTR getid_keyret

POLL1A: ; key 7
	;row2
	cmp AL, 00000010b
	jne POLL1B
	mov DS:AUDIO_START, 89C4H
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1

	mov DS:keyin, 7
	jmp NEAR ptr getid_keyret
	
POLL1B: ; key 4
	;row3
	cmp AL, 00000100b
	jne POLL1C
	mov DS:AUDIO_START, 8546H
	mov DS:AUDIO_CNT, 5600
	mov DS:AUDIO_FLAG, 1

	mov DS:keyin, 4
	jmp near ptr getid_keyret
	
POLL1C: ; key 1
	;row4
	cmp AL, 00001000b
	jne POLL2
	mov DS:AUDIO_START, 8190H
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 1
	jmp near ptr getid_keyret
	
POLL2:	; key 0
	mov DX, PORTB8255
	mov AX, 00000010b
	out DX, AL
	
	mov DX, PORTC8255
	in AX, DX
	
	; Check for assertions 2
	;row1
	cmp AL, 00000001b
	jne POLL2A
	mov DS:AUDIO_START, 8000H
	mov DS:AUDIO_CNT, 6400
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 0
	jmp NEAR PTR getid_keyret

POLL2A:	; key 8
	;row2
	cmp AL, 00000010b
	jne POLL2B
	mov DS:AUDIO_START, 8AF0H
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 8
	jmp NEAR PTR getid_keyret
	
POLL2B: ; key 5
	;row3
	cmp AL, 00000100b
	jne POLL2C
	mov DS:AUDIO_START, 86A4H
	mov DS:AUDIO_CNT, 6400
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 5
	jmp NEAR PTR getid_keyret

POLL2C:	; key 2
	;row4
	cmp AL, 00001000b
	jne POLL3
	mov DS:AUDIO_START, 82BCH
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1
	
	
	mov DS:keyin, 2
	jmp NEAR PTR getid_keyret

POLL3: ; key 11
	mov DX, PORTB8255
	mov AX, 00000100b
	out DX, AL
	
	mov DX, PORTC8255
	in AX, DX
	
	;check for assertions 3 
	;row1
	cmp AL, 00000001b
	jne POLL3A
	mov DS:AUDIO_START, 0AFA8H
	mov DS:AUDIO_CNT, 3600
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 11
	jmp NEAR PTR getid_keyret

POLL3A:	; key 9
	;row2
	cmp AL, 00000010b
	jne POLL3B
	
	mov DS:AUDIO_START, 8C1CH
	mov DS:AUDIO_CNT, 4800
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 9
	jmp NEAR PTR getid_keyret
	
POLL3B: ; key 6
	;row3
	cmp AL, 00000100b
	jne POLL3C
	
	
	mov DS:AUDIO_START, 8834H
	mov DS:AUDIO_CNT, 6400
	mov DS:AUDIO_FLAG, 1
	

	mov DS:keyin, 6
	jmp NEAR PTR getid_keyret

POLL3C:	; key 3
	;row4
	cmp AL, 00001000b
	jne POLL4
	
	mov DS:AUDIO_START, 83E8H
	mov DS:AUDIO_CNT, 5600
	mov DS:AUDIO_FLAG, 1
	
	mov DS:keyin, 3
	jmp NEAR PTR getid_keyret
	
POLL4:
	mov DX, PORTB8255
	mov AX, 00001000b
	out DX, AL
	
	;check for assertions 4

	
POLL5:
	mov DX, PORTB8255
	mov AX, 00010000b
	out DX, AL

	;check for assertions 5

POLL6:	
	mov DX, PORTB8255
	mov AX, 00100000b
	out DX, AL
	
	;check for assertions 6
	
	jmp POLL1

; ----------------Start of procedure PRINT_2HEX ------------------------
PRINT_2HEX 	PROC	FAR
	QUE_BASE	EQU	OFFSET QUEUE_TRANS
; The byte to be printed as 2 HEX number is put into AL.
; This procedure is then called.
		CALL	FAR PTR CHAR2HEX
; Result is return in AX
		PUSH 	AX
		MOV	AL,AH
		CALL	FAR PTR PRINT_CHAR
		POP	AX
		CALL	FAR PTR PRINT_CHAR
		RET
PRINT_2HEX	ENDP

;------------------Start of Procedure CHAR2HEX ----------------------------
CHAR2HEX	PROC	FAR
; Char to be converted to HEX is put in AL before calling this procedure.
; HEX version is return in AX.
		MOV	AH,AL
		AND	AL,00001111B
		CMP	AL,9
		JG	GT9_1
		OR	AL,00110000B
		JMP 	DIGIT2
GT9_1:		SUB	AL,9
		OR	AL,01000000B
DIGIT2:
		SHR	AH,4
		CMP	AH,9
		JG	GT9_2
		OR	AH,00110000B
		JMP	DONE
GT9_2:		SUB	AH,9
		OR	AH,01000000B
DONE:
		RET
CHAR2HEX	ENDP

; ---------------- Start of procedure PRINT_CHAR ------------------------
PRINT_CHAR	PROC	FAR
; This procedure is called to put a character into queue for transmission
; through the serial port. 
; The data to be transmitted is put in AL before the procedure is called.
; Data is put at the tail. Queue_tail is then inc to point to next loc.
; Data is taken from the head. Queue_head is then inc to point to next data.
	
		PUSH	BX			;Save BX	
		PUSH	ES

		PUSH	AX

		MOV	BX,SEG QUEUE_TAIL	;Init segment register.
		MOV	ES,BX			;ES now contains seg of INT_RAM_AREA

		IN	AL,SIER 		;disable TX interrupt.
		AND	AL,11111101B
		OUT	SIER,AL

		POP	AX
		MOV	BX,ES:QUEUE_TAIL
		MOV	ES:QUE_BASE[BX],AL	;Put data to queue_tail.
		INC	ES:QUEUE_TAIL		;Increment queue_tail
		CMP	ES:QUEUE_TAIL,QUEUE_LEN	;and wrap around
		JL	L_PRINT1		;to zero if needed.
		MOV	ES:QUEUE_TAIL,0
L_PRINT1:	
		IN	AL,SIER			;enable TX interrupt
		OR	AL,00000010B
		OUT	SIER,AL

		POP	ES
		POP	BX
		RET
PRINT_CHAR	ENDP

; Behavior depends on a state machine
; Token_state: wait for the token
; add later 
SERIAL_REC_ACTION	PROC	FAR
	PUSH	CX
	PUSH 	BX
	PUSH	DS
	push	ax
	
	cmp DS:RECV_FLAG, 0
	jne VALID
	inc DS:RECV_FLAG
	jmp rec_ret

	Valid:
		cmp DS:rec_state, 0
		je Token_state

		Token_state:
			xor BX, BX
			mov BL, DS:RECV_INDEX
			mov DS:RECV_BYTES[BX], AL
			inc DS:RECV_INDEX
			
			cmp DS:RECV_INDEX, 2
			jb rec_ret

			cmp DS:RECV_BYTES[1], 0
			mov DS:RECV_INDEX, 0
			; not my turn
			jne rec_ret

		Send_state:
			; check if we have something to send
			cmp DS:send_ready, 0
			je send_nack ; nothing to send

			; these lines are not needed for now
			; ; check if id/pw is ready
			; cmp DS:send_ready, 1
			; je send_pwid
			send_pwid:
				; |ACK2|DID|OP|UID|PW|
				; Send ACK2
				mov al, ACK2
				call far ptr print_char
				; Send DID
				mov al, 0
				call far ptr print_char
				; Send OP
				mov al, 0CH
				call far ptr print_char
				; Send UID
				xor al, al
				mov al, DS:userid
				shl al, 4
				or al, DS:userid[1]
				call far ptr print_char

				xor al, al
				mov al, DS:userid[2]
				shl al, 4
				or al, DS:userid[3]
				call far ptr print_char
				; Send PIN
				xor al, al
				mov al, DS:userpw
				shl al, 4
				OR al, DS:userpw[1]
				call far ptr print_char

				xor al, al
				mov al, DS:userpw[2]
				shl al, 4
				OR al, DS:userpw[3]
				call far ptr print_char

				; everything sent, reset send_ready
				mov DS:send_ready, 0

				jmp rec_ret

			send_nack:
				mov al, ACK1
				call far ptr print_char

	rec_ret:
		pop ax
		POP	DS
		POP	BX
		POP	CX
		RET
SERIAL_REC_ACTION	ENDP
	
	
TIMER2_ISR proc far
	push AX
	push BX
	push DX
	xor BX, BX

	cmp DS:AUDIO_FLAG, 1
	jne LED_SECT			; No Audio to play
	
	mov DX, DS:AUDIO_START
	mov ES, DX
	
AUDIO_SECT:
	mov AL, ES:[DI]
	inc DI
	
	cmp DI, DS:AUDIO_CNT
	jne LED_SECT			; If not finished, don't clear flag and DI
	
	mov DS:AUDIO_FLAG, 0
	xor DI, DI

	
LED_SECT:
	mov AL, DS:T2_CNT
	cmp AL, 27
	
	jne SKIP ;skip the LEDs
	
	; choose the led display
	xor BX, BX
	mov BL, DS:LED_INDEX
	
	mov AL, DS:LED_SELECT[BX]
	mov DX, CHOOSE_LED
	out DX, AL
	
	; output the segments
	mov AL, DS:LED_DISP[BX]
	mov DX, CHOOSE_SEG
	out DX, AL
	
	inc BL
	cmp BL, 6
	jne NO_RESTART
	mov BL, 0
	
NO_RESTART:
	mov DS:LED_INDEX, BL	
	mov DS:T2_CNT, 0
	
SKIP:
	inc DS:T2_CNT
	
	; Send EOI
	mov DX, EOI
	mov AX, 08H
	out DX, AL
	
	pop DX
	pop BX
	pop AX
	iret
TIMER2_ISR endp	

SERIAL_ISR proc far
		PUSH	AX			;save registers
		PUSH	BX
        PUSH    DX
               	               
		IN	AL,IIR			;read in serial INT ID
        AND     AL,00000111B
		CMP     AL,00000100B		;check if rx interrupt
		JE	RECEIVE_INTR

		CMP    	AL,00000010B		;check if tx interrupt
		JE	TRANSMIT_INTR

		
;RESET_INT_CTL
                MOV DX, EOI
                MOV AX, 12
                OUT DX, AL

                POP     DX
		POP	BX			;false serial interrupt
		POP	AX
		IRET				;return
SERIAL_ISR endp

RECEIVE_INTR:	
              
		IN	AL,SRB			
; Information received will be used by user routine
; Action to be taken will be contained in SERIAL_REC_ACTION
		CALL	FAR PTR SERIAL_REC_ACTION
		
		MOV DX, EOI
		MOV AX, 12
		OUT DX, AL
		POP DX
		POP	BX			;false serial interrupt
		POP	AX
		IRET

TRANSMIT_INTR:
               
		PUSH	ES			;save ES
		MOV	BX,SEG QUEUE_TAIL	;set ES to SERIAL_Q_SEG
		MOV	ES,BX
		MOV	BX,ES:QUEUE_TAIL
		CMP	BX,ES:QUEUE_HEAD	;more data to be transmitted?
		JE	L_TX2
		MOV	BX,ES:QUEUE_HEAD	;get data from queue
		MOV	AL,ES:QUE_BASE[BX]
		OUT	STB,AL			;tx data
		INC	ES:QUEUE_HEAD		;point to next data
		CMP	ES:QUEUE_HEAD,QUEUE_LEN ;wrap around if necessary
		JL	L_TX1
		MOV	ES:QUEUE_HEAD,0
L_TX1:		
		MOV	BX,ES:QUEUE_TAIL
		CMP	BX,ES:QUEUE_HEAD	;more data to be transmitted?
		JNE	L_TX3
L_TX2:
		IN	AL,SIER			;no more, disable TX interrupt.
		AND    	AL,11111101B
		OUT	SIER,AL
L_TX3:
		
;RESET_INT_CTL
                MOV DX, EOI
                MOV AX, 12
                OUT DX, AL
      		POP	ES			;restore original ES(transmit)
		
                POP     DX 
                POP	BX			;return serial interrupt
		POP	AX
		IRET
; **************** End of SERIAL_INTR service routine ************************

CODE_SEG        ENDS
END